home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / NvidiaDetector / nvidiadetector.py
Encoding:
Python Source  |  2009-04-20  |  14.7 KB  |  402 lines

  1. #
  2. #       nvidiadetector.py
  3. #       
  4. #       Copyright 2008 Alberto Milone <albertomilone@alice.it>
  5. #       
  6. #       This program is free software; you can redistribute it and/or modify
  7. #       it under the terms of the GNU General Public License as published by
  8. #       the Free Software Foundation; either version 2 of the License, or
  9. #       (at your option) any later version.
  10. #       
  11. #       This program is distributed in the hope that it will be useful,
  12. #       but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. #       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. #       GNU General Public License for more details.
  15. #       
  16. #       You should have received a copy of the GNU General Public License
  17. #       along with this program; if not, write to the Free Software
  18. #       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  19. #       MA 02110-1301, USA.
  20.  
  21. import os
  22. import re
  23. import subprocess
  24. from subprocess import Popen, PIPE
  25. import sys, logging
  26.  
  27. aliasesPath = '/usr/share/jockey/modaliases/'
  28.  
  29. class NoDatadirError(Exception):
  30.     "Exception thrown when no modaliases dir can be found"
  31.  
  32. class NvidiaDetection(object):
  33.     '''
  34.     A simple class to:
  35.       * Detect the available graphics cards
  36.       * See what drivers support them (If they are 
  37.         NVIDIA cards). If more than one card is 
  38.         available, try to find the highest common 
  39.         driver version which supports them all.
  40.         (READ the comments in the code for further
  41.         details)
  42.       * Return the recommended driver version
  43.     '''
  44.     oldPackages = ['nvidia-glx',
  45.                    'nvidia-glx-new',
  46.                    'nvidia-glx-legacy',
  47.                    'nvidia-glx-envy',
  48.                    'nvidia-glx-new-envy',
  49.                    'nvidia-glx-legacy-envy',
  50.                    'nvidia-glx-177']
  51.  
  52.     def __init__(self, printonly=None, verbose=None, datadir=aliasesPath):
  53.         '''
  54.         printonly = if set to None will make an instance
  55.                     of this class return the selected
  56.                     driver.
  57.                     If set to True it won't return
  58.                     anything. It will simply and print
  59.                     the choice.
  60.                     
  61.         verbose   = if set to True will make the methods
  62.                     print what is happening.
  63.         '''
  64.         
  65.         
  66.         self.printonly = printonly
  67.         self.verbose = verbose
  68.         '''
  69.         If the modaliases path does not exist
  70.         print none, so that debconf is not triggered.
  71.         '''
  72.         if not os.path.isdir(datadir):
  73.             print 'none'
  74.             logging.debug("dir %s not found" % datadir)
  75.             # Exit without an error exit status
  76.             # so as not to compromise dist-upgrades
  77.             # if the modaliases are not installed.
  78.             raise NoDatadirError, "datadir '%s' not found" % datadir
  79.         self.datadir = datadir
  80.         self.detection()
  81.         self.getData()
  82.         self.getCards()
  83.         self.removeUnsupported()
  84.         if printonly == True:
  85.             self.printSelection()
  86.         else:
  87.             self.selectDriver()
  88.  
  89.     def detection(self):
  90.         '''
  91.         Detect the models of the graphics cards
  92.         and store them in self.cards
  93.         '''
  94.         self.cards = []
  95.         p1 = Popen(['lspci', '-n'], stdout=PIPE)
  96.         p = p1.communicate()[0].split('\n')
  97.         indentifier1 = re.compile('.*0300: *(.+):(.+) \(.+\)')
  98.         indentifier2 = re.compile('.*0300: *(.+):(.+)')
  99.         for line in p:
  100.             m1 = indentifier1.match(line)
  101.             m2 = indentifier2.match(line)
  102.             if m1:
  103.                 id1 = m1.group(1).strip().lower()
  104.                 id2 = m1.group(2).strip().lower()
  105.                 id = id1 + ':' + id2
  106.                 self.cards.append(id)
  107.             elif m2:
  108.                 id1 = m2.group(1).strip().lower()
  109.                 id2 = m2.group(2).strip().lower()
  110.                 id = id1 + ':' + id2
  111.                 self.cards.append(id)
  112.  
  113.     def getData(self):
  114.         '''
  115.         Get the data from the modaliases for each driver
  116.         and store them in self.drivers
  117.         '''
  118.         
  119.         files = os.listdir(self.datadir)
  120.         self.drivers = {}
  121.         
  122.         for file in files:
  123.             a = open(self.datadir + file, 'r')
  124.             lines = a.readlines()
  125.             indentifier = re.compile('.*alias pci:v0000(.+)d0000(.+)sv.*nvidia.*nvidia-glx-(.+).*')
  126.             for line in lines:
  127.                 m1 = indentifier.match(line)
  128.                 if m1:
  129.                     id1 = m1.group(1).strip().lower()#e.g. 10de or 12d2 for nvidia
  130.                     id2 = m1.group(2).strip().lower()#the id which is specific to the model
  131.                     drivername = m1.group(3).strip().lower()#e.g. 173, 177, 96 or 71
  132.                     fullname = 'nvidia-glx-%s' % drivername.strip()
  133.                     if id1 in ['10de', '12d2'] and fullname not in self.oldPackages:#if NVIDIA
  134.                         self.drivers.setdefault(int(drivername), []).append(id1 + ':' + id2)
  135.             a.close()
  136.         
  137.         
  138.         '''
  139.         If the modaliases files don't contain anything useful
  140.         just print none and exit so as not to trigger debconf.
  141.         '''
  142.         if len(self.drivers.keys()) == 0:
  143.             print 'none'
  144.             #raise ValueError, "modaliases have no useful information"
  145.     
  146.     def getCards(self):
  147.         '''
  148.         See if the detected graphics cards are NVIDIA cards.
  149.         If they are NVIDIA cards, append them to self.nvidiaCards
  150.         '''
  151.         self.driversForCards = {}
  152.         self.nvidiaCards = []
  153.         '''
  154.         It is possible to override hardware detection (only for testing
  155.         purposes) by setting self.cards to one of the following lists:
  156.         
  157.         self.cards = ['10de:02e2', '10de:002d', '10de:0296', '10de:087f']
  158.         self.cards = ['10de:02e2', '10de:087f']
  159.         self.cards = ['10de:02e2', '10de:087f', '10de:fake']
  160.         self.cards = ['10de:fake']
  161.         '''
  162.         
  163.         for card in self.cards:
  164.             if card[0: card.find(':')] == '10de':
  165.                 if self.verbose:
  166.                     print 'NVIDIA card found (' + card + ')'
  167.                 self.nvidiaCards.append(card)
  168.  
  169.         self.orderedList = self.drivers.keys()
  170.         self.orderedList.sort(reverse=True)
  171.  
  172.         '''
  173.         See what drivers support each card and fill self.driversForCards
  174.         so as to have something like the following:
  175.         
  176.         self.driversForCards = {
  177.                                  'id_of_card1': [driver1, driver2],
  178.                                  'id_of_card2': [driver2, driver3],
  179.                                }
  180.         '''
  181.         for card in self.nvidiaCards:
  182.             supported = False
  183.             for driver in self.orderedList:
  184.                 if card in self.drivers[driver]:
  185.                     supported = True
  186.                     if self.verbose:
  187.                         print 'Card', card, 'supported by driver', driver
  188.                     self.driversForCards.setdefault(card, []).append(driver)
  189.             if supported == False:
  190.                 self.driversForCards.setdefault(card, []).append(None)
  191.  
  192.     def removeUnsupported(self):
  193.         '''
  194.         Remove unsupported cards from self.nvidiaCards and from
  195.         self.driversForCards
  196.         '''
  197.         unsupportedCards = []
  198.         for card in self.driversForCards:
  199.             if None in self.driversForCards[card]:
  200.                 unsupportedCards.append(card)
  201.  
  202.         for unsupported in unsupportedCards:
  203.             if self.verbose:
  204.                 print 'Removing unsupported card', unsupported
  205.             self.nvidiaCards.remove(unsupported)
  206.             del self.driversForCards[unsupported]
  207.  
  208.     def selectDriver(self):
  209.         '''
  210.         If more than one card is available, try to get the highest common driver
  211.         '''
  212.         cardsNumber = len(self.nvidiaCards)
  213.         if cardsNumber > 0:#if a NVIDIA card is available
  214.             if cardsNumber > 1:#if more than 1 card
  215.                 '''
  216.                 occurrence stores the number of occurrences (the values of the
  217.                 dictionary) of each driver version (the keys of the dictionary)
  218.                 
  219.                 Example:
  220.                     occurrence = {177: 1, 173: 3}
  221.                     This means that driver 177 supports only 1 card while 173
  222.                     supports 3 cards.
  223.                 '''
  224.                 occurrence = {}
  225.                 for card in self.driversForCards:
  226.                     for drv in self.driversForCards[card]:
  227.                         occurrence.setdefault(drv, 0)
  228.                         occurrence[drv] += 1
  229.                 
  230.                 occurrences = occurrence.keys()
  231.                 occurrences.sort(reverse=True)
  232.                 '''
  233.                 candidates is the list of the likely candidates for the
  234.                 installation
  235.                 '''
  236.                 candidates = []
  237.                 for driver in occurrences:
  238.                     if occurrence[driver] == cardsNumber:
  239.                         candidates.append(driver)
  240.                 if len(candidates) > 0:
  241.                     '''
  242.                     If more than one driver version works for all the available
  243.                     cards then the newest one is selected.
  244.                     
  245.                     USE-CASE:
  246.                         If a user has the following cards:
  247.                         * GeForce 9300 (supported by driver 177 and 173)
  248.                         * GeForce 7300 (supported by driver 177 and 173)
  249.                         * GeForce 6200 (supported by driver 177 and 173)
  250.                     
  251.                         Driver 177 is selected.
  252.                     '''
  253.                     candidates.sort(reverse=True)
  254.                     choice = candidates[0]
  255.                     if self.verbose and not self.printonly:
  256.                         print 'Recommended NVIDIA driver:', choice
  257.                 else:
  258.                     '''
  259.                     Otherwise, if there is no single driver version which works 
  260.                     for all the available cards, the newest one is selected.
  261.                     
  262.                     USE-CASE:
  263.                         If a user has the following cards:
  264.                         * GeForce 9300 (supported by driver 177 and 173)
  265.                         * GeForce 1 (supported by driver 71)
  266.                         * GeForce 2 (supported by driver 71)
  267.                         
  268.                         The most modern card has the highest priority since
  269.                         no common driver can be found. The other 2 cards 
  270.                         should use the open source driver
  271.                     '''
  272.                     choice = occurrences[0]
  273.                     if self.verbose and not self.printonly:
  274.                         print 'Recommended NVIDIA driver:', choice
  275.             else:#just one card
  276.                 '''
  277.                 The choice is easy if only one card is available and/or supported.
  278.                 
  279.                 The newest driver which supports the card is chosen.
  280.                 '''
  281.                 choice = self.driversForCards[self.driversForCards.keys()[0]][0]
  282.                 if self.verbose and not self.printonly:
  283.                     print 'Recommended NVIDIA driver:', choice
  284.             '''
  285.             FIXME: we should use a metapackage for this
  286.             '''
  287.             choice = 'nvidia-glx-' + str(choice)
  288.         else:
  289.             '''
  290.             If no card is supported
  291.             '''
  292.             if self.verbose:
  293.                 print 'No NVIDIA package to install'
  294.             choice = None
  295.             
  296.         return choice
  297.     
  298.     def checkpkg(self, pkglist):
  299.         '''
  300.         USAGE:
  301.             * pkglist is the list of packages  you want to check
  302.             * use lists for one or more packages
  303.             * use a string if it is only one package
  304.             * lists will work well in both cases
  305.         '''
  306.         '''
  307.         Checks whether all the packages in the list are installed
  308.         and returns a list of the packages which are not installed
  309.         '''
  310.         lines = []
  311.         notinstalled = []
  312.         p1 = Popen(['dpkg', '--get-selections'], stdout=PIPE)
  313.         p = p1.communicate()[0]
  314.         c = p.split('\n')
  315.         for line in c:
  316.             if line.find('\tinstall') != -1:#the relevant lines
  317.                 lines.append(line.split('\t')[0])
  318.         if self.isstr(pkglist) == True:#if it is a string
  319.             try:
  320.                 if lines.index(pkglist):
  321.                     pass
  322.             except ValueError:
  323.                 notinstalled.append(pkglist)
  324.         else:#if it is a list
  325.             for pkg in pkglist:
  326.                 try:
  327.                     if lines.index(pkg):
  328.                         pass
  329.                 except ValueError:
  330.                     notinstalled.append(pkg)
  331.  
  332.         return notinstalled
  333.     
  334.     def isstr(self, elem):
  335.         return isinstance(elem, type('')) or isinstance(elem, type(u''))
  336.     
  337.     def islst(self, elem):
  338.         return isinstance(elem, type(())) or isinstance(elem, type([]))
  339.     
  340.     def getDrivers(self):
  341.         '''
  342.         oldPackages = a list of the names of the obsolete drivers
  343.         notInstalled = a list of the obsolete drivers which are not
  344.                        installed
  345.         '''
  346.         installedPackage = None
  347.         notInstalled = self.checkpkg(self.oldPackages)
  348.         for package in self.oldPackages:
  349.             if package not in notInstalled:
  350.                 installedPackage = package
  351.         return (len(notInstalled) != len(self.oldPackages))
  352.     
  353.     def printSelection(self):
  354.         '''
  355.         Part for the kernel postinst.d/ hook
  356.         '''
  357.         driver = self.selectDriver()
  358.         if self.getDrivers():#if an old driver is installed
  359.             if driver:#if an appropriate driver is found
  360.                 print driver
  361.             else:
  362.                 print 'none'
  363.         else:
  364.             #print driver
  365.             print 'none'
  366.  
  367. #def usage():
  368. #    instructionsList = ['The only accepted parameters are:'
  369. #    '\n  --printonly', '\tprint the suggested driver'
  370. #    
  371. #    '\n  --verbose', '\t\teach step will be verbose'
  372. #    ]
  373. #    print ''.join(instructionsList)
  374.  
  375. #def main():
  376. #    err = 'Error: parameters not recognised'
  377. #    try:
  378. #        opts, args = getopt.getopt(sys.argv[1:], 'hp:v', ['help', 'printonly', 'verbose'])
  379. #    except getopt.GetoptError, err:
  380. #        # print help information and exit:
  381. #        print str(err) # will print something like 'option -a not recognized'
  382. #        usage()
  383. #        sys.exit(2)
  384. #    printonly = None
  385. #    verbose = None
  386. #    for o, a in opts:
  387. #        if o in ('-v', '--verbose'):
  388. #            verbose = True
  389. #        elif o in ('-p', '--printonly'):
  390. #            printonly = True
  391. #        elif o in ('-h', '--help'):
  392. #            usage()
  393. #            sys.exit()
  394. #        else:
  395. #            assert False, 'unhandled option'
  396. #    a = NvidiaDetection(printonly=printonly, verbose=verbose)
  397.  
  398.  
  399. #if __name__ == '__main__':
  400. #    main()
  401.  
  402.